package com.bigFuWengBackend.server;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bigFuWengBackend.entity.Constant;
import com.bigFuWengBackend.entity.Item;
import com.bigFuWengBackend.entity.Room;
import com.bigFuWengBackend.entity.User;
import com.bigFuWengBackend.entity.login.LoginRecieveDTO;
import com.bigFuWengBackend.entity.room.JoinRoomRecDTO;
import com.bigFuWengBackend.entity.room.JoinRoomSendDTO;
import com.bigFuWengBackend.entity.room.OtherUserJoinSendDTO;
import com.bigFuWengBackend.mapper.ItemMapper;
import com.bigFuWengBackend.mapper.UserInfoMapper;
import com.bigFuWengBackend.server.handler.ItemsHandler;
import com.bigFuWengBackend.server.handler.LoginHandler;
import com.bigFuWengBackend.server.handler.RoomHandler;
import com.bigFuWengBackend.utils.SnowFlake;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


@Slf4j
@ServerEndpoint("/ws")
@Component
public class WebSocketServer {
    private String userId;
    private Session session;
    private User user;
    private static List<Room> rooms = new ArrayList<>();
    private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    /**
     * 用来解决webSocket中无法注入mapper
     */
    private static ApplicationContext applicationContext;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        WebSocketServer.applicationContext = applicationContext;
    }
    /**
     * A,B与服务器建立连接
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        UserInfoMapper userInfoMapper = applicationContext.getBean(UserInfoMapper.class);
        session.setMaxTextMessageBufferSize(10*1024*1024);
        session.setMaxBinaryMessageBufferSize(10*1024*1024);
        this.session = session;
        log.info("连接成功");
    }

    /**
     * 链接关闭
     */
    @OnClose
    public void onClose(Session session) {
        log.info("断开连接");
    }

    /**
     * 向session发送消息
     */
    @OnMessage
    public void sendMessage(String message,Session session) throws IOException {
        System.out.println(message);
        JSONObject jsonObject =  JSON.parseObject(message);
        String type = (String)jsonObject.get("type");

        if(type.equals(Constant.LOGIN_TYPE)){
            LoginHandler loginHandler = new LoginHandler();
            LoginRecieveDTO recieve = JSON.parseObject(message, LoginRecieveDTO.class);
            userId = recieve.getData().getUserId();
            webSocketMap.put(userId,this);
            String res = loginHandler.getUserInfo(message, applicationContext.getBean(UserInfoMapper.class));
            sendMessage(res);

            //得到用户，加入线程
            User user = new User();
            user.setUserId(userId);
            user.setSession(this.session);

            this.user = user;
            this.userId = userId;
        }
        else if(type.equals(Constant.GET_ALL_ITEMS_TYPE)){
            ItemsHandler itemsHandler = new ItemsHandler();
            String res = itemsHandler.getItemsAll(message, applicationContext.getBean(UserInfoMapper.class));
            sendMessage(res);
        }
        else if(type.equals("itemsSetStatus")){
            ItemsHandler itemsHandler = new ItemsHandler();
            itemsHandler.getItemsAll(message, applicationContext.getBean(UserInfoMapper.class));
        }
        else if(type.equals(Constant.USER_JOIN_ROOM)){
            processUserJoinRoom(message, session);
        }
        else if(type.equals("roomItemBuy")){
            RoomHandler roomHandler = new RoomHandler();
            String res = roomHandler.roomItemBuy(message, applicationContext.getBean(UserInfoMapper.class));
            GroupSending(res);
        }
        else if(type.equals("roomItemOnSell")){
            RoomHandler roomHandler = new RoomHandler();
            String res = roomHandler.roomItemOnSell(message, applicationContext.getBean(UserInfoMapper.class));
            GroupSending(res);
        }
        else if(type.equals("roomUserDied")){
            RoomHandler roomHandler = new RoomHandler();
            String res = roomHandler.roomUserDied(message, applicationContext.getBean(UserInfoMapper.class));
            GroupSending(res);
        }
        else if(type.equals("roomUserExit")){
            RoomHandler roomHandler = new RoomHandler();
            String res = roomHandler.roomUserExit(message, applicationContext.getBean(UserInfoMapper.class));
            GroupSending(res);
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }
    public void GroupSending(String message){
        for (String name : webSocketMap.keySet()){
            try {
                webSocketMap.get(name).sendMessage("123");
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    /**
     * 指定发送
     * @param name
     * @param message
     */
    public void AppointSending(String name,String message){
        try {
            webSocketMap.get(name).sendMessage("123");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public void sendMessage(String message) throws IOException {
//        log.info("发送消息"+message);
        this.session.getBasicRemote().sendText(message);
    }

    private Room getAvailableRoom() {
        for (Room room : rooms) {
            if (!room.isFull()) {
                return room;
            }
        }
        return null;
    }
    private Room createNewRoom() {
        SnowFlake snowFlake = new SnowFlake(12,12);
        Room room = new Room();
        room.setRoomId(String.valueOf(snowFlake.getNextId()));
        rooms.add(room);
        return room;
    }
    private User getPlayerBySession(Session session) {
        for (Room room : rooms) {
            for (User player : room.getUsers()) {
                if (player.getSession().equals(session)) {
                    return player;
                }
            }
        }
        return null;
    }
    private void processUserJoinRoom(String message,Session session) throws IOException {
        ItemMapper itemMapper = applicationContext.getBean(ItemMapper.class);
        UserInfoMapper userInfoMapper = applicationContext.getBean(UserInfoMapper.class);
        //解析数据，得到DTO
        RoomHandler roomHandler = new RoomHandler();
        JoinRoomRecDTO recieve = roomHandler.userJoinRoom(message, applicationContext.getBean(UserInfoMapper.class));

        //解析得到对象
        User userRecieve = recieve.getData().getUser();
        BigDecimal userMoneyRecieve = recieve.getData().getUserMoney();
        Map<String, Item> userItemRecieve = recieve.getData().getUserItem();

        //User加入房间，得到房间
        Room room = getAvailableRoom();
        if (room == null) {
            room = createNewRoom();
        }
        //封装返回给User的数据，包括房间的原始信息
        JoinRoomSendDTO res = new JoinRoomSendDTO();

        res.setType(Constant.USER_JOIN_ROOM);
//        room.users.add(this.user);
        room.setItemPool(room.getItemPool());
        res.setRoom(room);
        System.out.println(res.getRoom().getRoomId());
        sendMessage(JSON.toJSONString(res));

        //封装返回给其他人的数据，包括新加入用户的原始带入房间数据
        OtherUserJoinSendDTO resBroadcast= new OtherUserJoinSendDTO();
        resBroadcast.setType(Constant.OTHER_USER_JOIN_ROOM);
        resBroadcast.setUser(userRecieve);
        room.broadcastMessage(JSON.toJSONString(resBroadcast));

        //改变服务端状态，User与房间双向绑定
        room.getItemPool().putAll(userItemRecieve);
        this.user.setRoom(room);

        //todo
        //扣除相关的钱和物品
        //扣钱
        userInfoMapper.costMoney(this.user.getUserId(), userMoneyRecieve);
        //扣物品
        for (String key : userItemRecieve.keySet()) {
            itemMapper.updateItemsStatus2carryIn(key);
        }
    }
}